package com.ionehe.group.common.uaa.ex.domain.service.impl;

import com.google.common.base.Throwables;
import com.ionehe.group.common.io.Response;
import com.ionehe.group.common.uaa.ex.domain.service.UaaCommonService;
import com.ionehe.group.common.uaa.ex.model.UaaManageInfo;
import com.ionehe.group.common.uaa.ex.utils.RedisUtils;
import com.ionehe.group.common.user.c.sdk.uaa.utils.JwtTokenUtil;
import com.ionehe.group.common.uaa.ex.dto.AdminUserDetails;
import com.ionehe.group.common.uaa.ex.convert.ManagerInfoConvert;
import com.ionehe.group.common.uaa.ex.dto.AdminLoginDTO;
import com.ionehe.group.common.uaa.ex.dto.FunctionDTO;
import com.ionehe.group.common.uaa.ex.dto.ManageInfoDTO;
import com.ionehe.group.common.uaa.ex.enums.ErrorMessages;
import com.ionehe.group.common.uaa.ex.exception.BusinessException;
import com.ionehe.group.common.uaa.ex.dao.ManageInfoDao;
import com.ionehe.group.common.uaa.ex.domain.service.AdminCacheReadService;
import com.ionehe.group.common.uaa.ex.domain.service.AdminCacheWriteService;
import com.ionehe.group.common.uaa.ex.domain.service.AdminReadService;
import com.ionehe.group.common.uaa.ex.domain.service.AdminWriteService;
import com.ionehe.group.common.uaa.ex.utils.LoginUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * Copyright (c) 2020 ionehe.com
 * Date: 2020/11/29
 * Time: 2:07 下午
 *
 * @author 2020年 <a href="mailto:a@ionehe.com">秀</a>
 */
@Service
@Slf4j
public class AdminWriteServiceImpl implements AdminWriteService {
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private AdminReadService adminReadService;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private AdminCacheReadService adminCacheReadService;
    @Autowired
    private AdminCacheWriteService adminCacheWriteService;
    @Autowired
    private LoginUtils loginUtil;
    @Resource
    private ManageInfoDao manageInfoDao;
    @Autowired
    private ManagerInfoConvert managerInfoConvert;
    @Autowired
    private UaaCommonService uaaCommonService;

    @Value("${redis.database:base}")
    private String redisDatabase;
    @Value("${redis.key.authCode:uaa:authCode}")
    private String redisKeyAuthCode;
    @Value("${redis.expire.authCode:300}")
    private Long redisExpireAuthCode;
    @Value("${redis.expire.common:86400}")
    private Long redisExpire;
    @Value("${redis.key.user:uaa:user}")
    private String redisKeyUser;
    @Value("${jwt.tokenLimit:-1}")
    private Integer tokenLimit;

    @Override
    public Response<ManageInfoDTO> register(ManageInfoDTO manageInfoDTO) {
        try {
            UaaManageInfo manage = new UaaManageInfo();
            manage.setAccount(manageInfoDTO.getAccount());
            manage.setIsDelete(false);
            UaaManageInfo manageInfo = manageInfoDao.selectOne(manage);
            if (Objects.nonNull(manageInfo)) {
                return Response.no(ErrorMessages.INSERT_USER_FAIL.toString());
            }
            UaaManageInfo mi = managerInfoConvert.dto2do(manageInfoDTO);
            mi.setCreateTime(new Date());
            mi.setPassword(passwordEncoder.encode(mi.getPassword()));
            Boolean isSuccess = manageInfoDao.insertSelective(mi) == 1;
            if (Boolean.TRUE.equals(isSuccess)) {
                return Response.yes(managerInfoConvert.do2dto(mi));
            }
            return Response.no(ErrorMessages.INSERT_USER_FAIL.toString());
        } catch (Exception e) {
            log.error("AdminWriteService[]register[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(ErrorMessages.INSERT_USER_FAIL.toString());
        }
    }

    @Override
    public Response<String> login(AdminLoginDTO adminLoginDTO) {
        log.info("AdminWriteService[]login[]start! adminLoginDTO:{}", adminLoginDTO);
        String token;
        try {
            String account = adminLoginDTO.getAccount();
            // 验证码校验
            Response<Boolean> res = uaaCommonService.verificationCodeCheck(adminLoginDTO.getKey(), adminLoginDTO.getCode());
            if (!res.success()) {
                return Response.no(res.getMessage());
            }

            //密码需要客户端加密后传递
            UserDetails userDetails = loadUserByUsername(account);
            passwordCheck(adminLoginDTO.getPassword(), userDetails.getPassword());
            statusCheck(userDetails.isEnabled());
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            token = jwtTokenUtil.generateToken(userDetails);
            loginUtil.tokenUpperLimitCheck(account, token);
            return Response.yes(token);
        } catch (AuthenticationException | BusinessException   e) {
            log.warn("AdminWriteService[]login[]error! cause:{}", e.getMessage());
            return Response.no(e.getMessage());
        } catch (Exception e) {
            log.error("AdminWriteService[]login[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(ErrorMessages.USER_LOGIN_FAIL.toString());
        }
    }

    @Override
    public Response<String> refreshToken(String oldToken) {
        return Response.yes(jwtTokenUtil.refreshHeadToken(oldToken));
    }

    @Override
    public Response<Boolean> update(ManageInfoDTO manageInfoDTO) {
        return null;
    }

    @Override
    public Response<Boolean> delete(Long id) {
        return null;
    }

    @Override
    public Response<Boolean> updateRole(Long adminId, List<Long> roleIds) {
        return null;
    }

    @Override
    public Response<Boolean> updatePassword(ManageInfoDTO updatePasswordParam) {
        return null;
    }

    /**********************************              service函数分界线             **************************************/

    private UserDetails loadUserByUsername(String username) {
        // 缓存中获取用户信息
        Response<ManageInfoDTO> res = adminCacheReadService.getAdmin(username);
        if (!res.success()) {
            throw new BusinessException(res.getMessage());
        }
        ManageInfoDTO manageInfoDTO = res.getData();

        // 数据库获取用户信息
        if (Objects.isNull(manageInfoDTO)) {
            UaaManageInfo manageInfo = new UaaManageInfo();
            manageInfo.setAccount(username);
            UaaManageInfo admin = manageInfoDao.selectOne(manageInfo);
            if(admin != null){
                manageInfoDTO =  managerInfoConvert.do2dto(admin);
                // 存入缓存中
                adminCacheWriteService.setAdmin(manageInfoDTO);
            }
        }

        // 资源权限信息
        if (manageInfoDTO != null) {
            List<FunctionDTO> resourceDTOList = adminReadService.getResourceList(manageInfoDTO.getAccount()).getData();
            return new AdminUserDetails(manageInfoDTO, resourceDTOList);
        }
        throw new BusinessException("用户名或密码错误");
    }

    /**
     * 帐号状态校验
     *
     * @param enabled 状态
     */
    private void statusCheck(boolean enabled) {
        if (!enabled) {
            throw new BusinessException("帐号已被禁用");
        }
    }

    /**
     * 密码校验
     *
     * @param pw       用户输入密码
     * @param password 数据库密码
     */
    private void passwordCheck(String pw, String password) {
        if (!passwordEncoder.matches(pw, password)) {
            throw new BusinessException("用户名或密码不正确");
        }
    }
}
